// CyTargetLinker,
// a Cytoscape plugin to extend biological networks with regulatory interaction
//
// Copyright 2011-2013 Department of Bioinformatics - BiGCaT, Maastricht University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package cytargetlinker;

import giny.model.Node;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import cytargetlinker.data.Interaction;
import cytargetlinker.data.RIN;
import cytargetlinker.data.ResultingNetwork;
import cytargetlinker.io.XgmmlParser;
import cytoscape.CyEdge;
import cytoscape.CyNode;
import cytoscape.Cytoscape;

/**
 * this class handles the RINs
 * load a RIN
 * show or hide a RIN
 * 
 */
public class RINHandler {
	
	private static RINHandler handler;
	
	public static RINHandler getHandler() {
		if(handler == null) {
			handler = new RINHandler();
		}
		return handler;
	}
	
	private RINHandler() {
	}
	
	/**
	 * loads and opens the rins
	 * TODO: allow other file types than xgmml
	 */
	public void loadNetworks(List<File> files, CyTargetLinker linker) {
		for(File file : files) {
			XgmmlParser parser = new XgmmlParser();
			try {
				RIN rin = parser.parseXgmmlFile(file, new ArrayList<String>(linker.getUserNetwork().getNodes().keySet()));
				
				if(rin.getEdges().size() == 0) {
					linker.setMessage(linker.getMessage() + rin.getName() + " | " + "no matching regulatory interactions found.\n");
				} else {
					linker.setMessage(linker.getMessage() + rin.getName() + " | " + rin.getEdges().size() + " regulatory interactions.\n");
					linker.getRins().add(rin);
				}
			} catch (Exception e) {
				linker.setMessage(linker.getMessage() + "WARNING: An error occured when reading " + file.getName() + ". This RIN will not be included in the analysis.\n");
			} 
		}
		
		Collections.sort(linker.getRins());
		for(int j = 0; j < linker.getRins().size(); j++) {
			linker.getRins().get(j).setColor(Preferences.getInstance().getColors().get(j));
		}
	}

    public void showRIN(RIN rin, CyTargetLinker linker) {
    	
    	if(!rin.isVisible()) {
    		for(CyEdge e : rin.getEdgesInvisible()) {
				linker.getResultNetwork().getNetwork().restoreEdge(e);
				rin.getEdgesVisible().add(e);
				restoreNode(e.getSource(), linker.getResultNetwork());
				restoreNode(e.getTarget(), linker.getResultNetwork());
			}
    		rin.getEdgesInvisible().clear();
    	}
		
		applyThreshold(linker);
	}
    
    public void hideRIN(RIN r, CyTargetLinker linker) {
    	// restore edges that are not hidden by the show/hide function
		for(RIN rin : linker.getRins()) {
			if(rin.isVisible() && !rin.getEdgesInvisible().isEmpty()) {
				for(CyEdge e : rin.getEdgesInvisible()) {
					linker.getResultNetwork().getNetwork().restoreEdge(e);
					rin.getEdgesVisible().add(e);
					restoreNode(e.getSource(), linker.getResultNetwork());
					restoreNode(e.getTarget(), linker.getResultNetwork());
				}
				rin.getEdgesInvisible().clear();
			}
		}
		
		for(CyEdge e : r.getEdgesVisible()) {
			r.getEdgesInvisible().add(e);
			linker.getResultNetwork().getNetwork().hideEdge(e);
		}
		r.getEdgesVisible().removeAll(r.getEdgesInvisible());
		
		List<CyNode> list = linker.getResultNetwork().getNetwork().nodesList();
		for(CyNode node : list) {
			if(linker.getResultNetwork().getNetwork().neighborsList(node).size() == 0) {
				hideNode(node, linker.getResultNetwork());
			}
		}
		
		r.setVisible(false);
		
		applyThreshold(linker);
    }
    
	
	/**
	 * hides all interactions between two nodes if not
	 * enough RINs support the interaction
	 */
	public void applyThreshold(CyTargetLinker linker) {		
		
		// restore edges that are not hidden by the show/hide function
		for(RIN rin : linker.getRins()) {
			if(rin.isVisible() && !rin.getEdgesInvisible().isEmpty()) {
				for(CyEdge e : rin.getEdgesInvisible()) {
					linker.getResultNetwork().getNetwork().restoreEdge(e);
					rin.getEdgesVisible().add(e);
					restoreNode(e.getSource(), linker.getResultNetwork());
					restoreNode(e.getTarget(), linker.getResultNetwork());
				}
				rin.getEdgesInvisible().clear();
			}
		}
		
		// hide all edges of RINs that are not hidden 
		for(Interaction i : linker.getResultNetwork().getInteractions()) {
			boolean hide = true;
			if(i.getEdges().size() >= linker.getThreshold()) {
				int count = 0;
				for(CyEdge e : i.getEdges().keySet()) {
					if(i.getEdges().get(e).isVisible()) {
						count++;
					}
				}
				if(count >= linker.getTreshhold()) {
					hide = false;
				}
			}
			if(hide) {
				for(CyEdge e : i.getEdges().keySet()) {
					i.getEdges().get(e).getEdgesInvisible().add(e);
					i.getEdges().get(e).getEdgesVisible().remove(e);
					linker.getResultNetwork().getNetwork().hideEdge(e);
				}
			}
		}
		
		List<CyNode> list = linker.getResultNetwork().getNetwork().nodesList();
		for(CyNode node : list) {
			if(linker.getResultNetwork().getNetwork().neighborsList(node).size() == 0) {
				hideNode(node, linker.getResultNetwork());
			}
		}
		
		linker.applyLayout();
		linker.updateTextFields();
		Cytoscape.getNetworkView(linker.getResultNetwork().getName()).redrawGraph(true, false);
	}
	
	private void restoreNode(Node node, ResultingNetwork network) {
		if(network.getHiddenNodes().contains(node)) {
			network.getNetwork().restoreNode(node.getRootGraphIndex()); 
	        network.getHiddenNodes().remove(node);
		}
	}
	
	private void hideNode(Node node, ResultingNetwork network) {
		if(!network.getNodesOriginal().contains(node) && !network.getHiddenNodes().contains(node)) {
			network.getNetwork().hideNode(node);
			network.getHiddenNodes().add(node);
		}
	}
}